#pragma once
#include "Common.hpp"

namespace SocketModule
{
    const int default_pending_num = 10;
    class Socket
    {
    protected:
        int _sockfd;
        InetAddr _addr;
        
        void CreateSocket(int type)
        {
            _sockfd = ::socket(AF_INET, type, 0);
            if(_sockfd == -1)
            {
                LOG(LogLevel::FATAL) << "socket: 申请套接字失败! " << strerror(errno);
                ::exit(SOCKET_ERROR);
            }
        }
        virtual void CreateSocket() = 0;
        virtual void Bind() = 0;
        virtual void Listen() = 0;
        virtual void Connect() = 0;
    public:
        Socket(const std::string& ip, in_port_t port) 
            : _sockfd(-1)
            , _addr(ip, port)
        {}
        // 用于TCP连接套接字
        Socket(const int sockfd, const InetAddr& addr)
            : _sockfd(sockfd)
            , _addr(addr)
        {}
        virtual ~Socket()
        {
            if(_sockfd != -1)
                ::close(_sockfd);
        }

        void Initialize()
        {
            CreateSocket();
            Bind();
            Listen();
            Connect();
        }

        virtual std::shared_ptr<TCPConnectSocket> Accept() = 0;
        virtual InetAddr Recive(std::string& recv_msg) = 0;
        virtual void Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) = 0;
    };

    // TCP监听套接字
    class TCPListenSocket : public Socket
    {
    public:
        TCPListenSocket(in_port_t port)
            : Socket("0.0.0.0", port)
        {}
        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_STREAM);
        }
        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            InetAddr client;
            int sockfd = ::accept(_sockfd, client.NetAddrPtr(), &client.AddrLen());
            if(sockfd == -1)
            {
                LOG(LogLevel::WARNING) << "accept: 建立连接失败! " << strerror(errno);
                return nullptr;
            }
            return std::make_shared<TCPConnectSocket>(sockfd, client);
        }

    private:
        void Bind() override
        {
            int n = ::bind(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
            if(n == -1)
            {
                LOG(LogLevel::FATAL) << "bind: 绑定地址信息失败! " << strerror(errno);
                ::exit(BIND_ERROR);
            }
        }
        void Listen() override
        {
            int n = ::listen(_sockfd, default_pending_num);
            if(n == -1)
            {
                LOG(LogLevel::FATAL) << "listen: 设置监听套接字失败! " << strerror(errno);
                ::exit(LISTEN_ERROR);
            }
        }
        void Connect() override
        {}
        InetAddr Recive(std::string& recv_msg) override
        {
            return InetAddr();
        }
        void Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {}
    };

    // TCP连接套接字
    class TCPConnectSocket : public Socket
    {
    public:
        TCPConnectSocket(int sockfd, const InetAddr& addr)
            :Socket(sockfd, addr)
        {}
        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_STREAM);
        }
        InetAddr Recive(std::string& recv_msg) override
        {
            char buffer[BUFFER_SIZE];
            ssize_t size = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            buffer[size] = 0;
            recv_msg = buffer;
            return _addr;
        }
        void Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            ssize_t size = ::send(_sockfd, send_msg.c_str(), send_msg.size(), 0);
        }
        const InetAddr& Client()
        {
            return _addr;
        }

    private:
        void Bind() override
        {}
        void Listen() override
        {}
        void Connect() override
        {}
        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            return nullptr;
        }
    };

    // TCP客户端
    class TCPClientSocket : public Socket
    {
    public:
        TCPClientSocket(const std::string& ip, in_port_t port)
            : Socket(ip, port)
        {}
        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_STREAM);
        }
        void Connect() override
        {
            int n = ::connect(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
            if(n == -1)
            {
                LOG(LogLevel::FATAL) << "connect: 连接失败! " << strerror(errno);
                ::exit(CONNECT_ERROR);
            }
            LOG(LogLevel::INFO) << "客户端套接字已连接到[" << _addr.Info() << "]";
        }
        InetAddr Recive(std::string& recv_msg) override
        {
            char buffer[BUFFER_SIZE];
            ssize_t size = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            buffer[size] = 0;
            recv_msg = buffer;
            return _addr;
        }
        void Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            ssize_t size = ::send(_sockfd, send_msg.c_str(), send_msg.size(), 0);
        }
    private:
        void Bind() override
        {}
        void Listen() override
        {}
        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            return nullptr;
        }
    };

    // UDP服务端
    class UDPServerSocket : public Socket
    {
    public:
        UDPServerSocket(const std::string& ip, in_port_t port)
            : Socket("0.0.0.0", port)
        {}
        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_DGRAM);
        }
        InetAddr Recive(std::string& recv_msg) override
        {
            InetAddr client;
            char buffer[BUFFER_SIZE];
            ssize_t size = ::recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, client.NetAddrPtr(), &client.AddrLen());
            client = InetAddr(client.NetAddr());
            buffer[size] = 0;
            recv_msg = buffer;
            return client;
        }
        void Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            ssize_t size = ::sendto(_sockfd, send_msg.c_str(), send_msg.size(), 0, peer.NetAddrPtr(), peer.AddrLen());
        }
    private:
        void Bind()
        {
            int n = ::bind(_sockfd, _addr.NetAddrPtr(), _addr.AddrLen());
            if(n == -1)
            {
                LOG(LogLevel::FATAL) << "bind: 绑定地址信息失败! " << strerror(errno);
                ::exit(BIND_ERROR);
            }
        }
        void Listen()
        {}
        void Connect()
        {}
        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            return nullptr;
        }
    };

    // UDP客户端
    class UDPClientSocket : public Socket
    {
    public:
        UDPClientSocket(const std::string ip, in_port_t port)
            : Socket(ip, port)
        {}
        void CreateSocket() override
        {
            Socket::CreateSocket(SOCK_DGRAM);
        }
        InetAddr Recive(std::string& recv_msg) override
        {
            InetAddr client;
            char buffer[BUFFER_SIZE];
            ssize_t size = ::recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, client.NetAddrPtr(), &client.AddrLen());
            client = InetAddr(client.NetAddr());
            buffer[size] = 0;
            recv_msg = buffer;
            return client;
        }
        void Send(const std::string& send_msg, const InetAddr& peer = InetAddr()) override
        {
            ssize_t size = ::sendto(_sockfd, send_msg.c_str(), send_msg.size(), 0, peer.NetAddrPtr(), peer.AddrLen());
        }
    private:
        void Bind()
        {}
        void Listen()
        {}
        void Connect()
        {}
        std::shared_ptr<TCPConnectSocket> Accept() override
        {
            return nullptr;
        }
    };
}